import { Asset, Component, js, NodePool, SpriteFrame } from "cc";
import BaseMgr from "../base/BaseMgr";
import { TimeUtils } from "../utils/TimeUtils";
import { UIUtils } from "../utils/UIUtils";
import { LogMgr } from "./LogMgr";
import { ResCache, ResCacheKey } from "./ResourceMgr";
/**
 * 资源缓存管理
 */
export class ResCacheMgr extends BaseMgr {
    private static instance: ResCacheMgr = null;
    protected constructor() {
        super()
    }

    public static getInstance(): ResCacheMgr {
        if (this.instance == null) {
            this.instance = new ResCacheMgr();

        }
        return this.instance;
    }

    //自动释放的资源
    private temporaryCache: ResCache[] = [];

    //非自动释放资源
    private permanentCache: ResCache[] = [];

    //是否已经开始计时释放
    private isStartDecRef: boolean = false;

    /**资源有效期分钟*/
    private assetsLifeTime: number = 5;

    private permanentCacheKeyMap: Map<string, ResCacheKey[]> = new Map()

    depsdeps: string[] = []
    init() {
        this.startDecRef();
    }
    /**
     * 开始计时释放
     * @param resCache 
     */
    private startDecRef() {
        if (!this.isStartDecRef) {
            this.isStartDecRef = true;
            UIUtils.schedule(this.autoRelease, this, 1)
        }

    }



    /**
     * 自动释放资源
     */
    autoRelease() {
        let nowTime = TimeUtils.GetTimeBySecond()//当前时间
        for (let i = 0; i < this.temporaryCache.length; i++) {
            let res = this.temporaryCache[i];
            //node销毁后的释放资源
            if (res && nowTime - res.releaseTime >= res.cacheTime) {//够时间了
                if (res.defCount <= 0) {
                    continue
                }
                let total = res.defCount;
                for (let j = 0; j < total; j++) {
                    res.asset.decRef();
                    LogMgr.getInstance().debug('引用计数减一', res.cachekey + " " + res.asset.refCount)
                    if (res.asset.refCount <= 0) {
                        this.temporaryCache.splice(i, 1);
                        i--
                        res.defCount = 0;
                        break;
                    }
                }
                if (res.asset.refCount > 0) {
                    res.releaseTime = TimeUtils.GetTimeBySecond() + 60 * 60 * 24;//重新设置时间
                }
            } else if (res && res.asset.refCount == 0) {
                this.temporaryCache.splice(i, 1);
                i--
                res.defCount = 0;
            }
        }

    }


    /**
     * 刷新资源时间
     * @param assets //需要刷新时间的资源集合
     */
    refreshTime(assets: ResCacheKey[]) {

        let nowTime: number = TimeUtils.GetTimeBySecond()//当前时间
        for (let j = 0; j < assets.length; j++) {
            for (let i = 0; i < this.temporaryCache.length; i++) {
                let as = assets[j];
                let tempS = this.temporaryCache[i]
                if (as.cachekey == tempS.cachekey && as.type == tempS.type) {
                    this.temporaryCache[i].releaseTime = nowTime
                    this.temporaryCache[i].defCount += as.defCount;
                    this.temporaryCache[i].canRelease = false
                    break;
                }
            }
        }
    }


    /**
     * 添加资源
     * @param res        资源类
     * @param autoRelese 是否自动释放
     */
    addAssets(res: ResCache, autoRelese: boolean) {
        if (!res || !res.asset) {
            return
        }
        let currentCache = autoRelese ? this.temporaryCache : this.permanentCache
        let existInd = this.isExistKey(res.cachekey, res.type, currentCache);
        if (existInd == -1) {
            currentCache.push(res)
        } else {//判断已存在
            currentCache[existInd] = res
        }


    }


    /**
     * 添加到待释放资源Map中，以便调用releaseAssetsFromMap手动释放资源
     * @param mapkey map key
     * @param bundle bundle名
     * @param path  资源路径
     * @param type  资源类型
     */
    addAssetKeyToMap(mapkey: string, bundle: string, path: string, type: typeof Asset) {
        let map = this.permanentCacheKeyMap.get(mapkey);
        let resCacheKeys = new ResCacheKey(bundle, path, type)
        if (map && map.length > 0) {
            map.push(resCacheKeys);
        } else {
            let arr: ResCacheKey[] = []
            arr.push(resCacheKeys);
            this.permanentCacheKeyMap.set(mapkey, arr)
        }

    }

    /**
     * 释放资源
     * @param mapKey 
     */
    releaseAssetsFromMap(mapKey: string) {
        let mapArr: ResCacheKey[] = this.permanentCacheKeyMap.get(mapKey);
        if (mapArr && mapArr.length > 0) {
            for (let j = 0; j < mapArr.length; j++)
                for (let i = 0; i < this.permanentCache.length; i++) {
                    let item = this.permanentCache[i];
                    if (item && item.cachekey == mapArr[j].cachekey && item.type == mapArr[j].type) {
                        item.asset.decRef();
                        if (item.asset.refCount <= 0) {
                            this.permanentCache.splice(i, 1);
                            i--;
                        }
                        break;
                    }
                }
        }
    }


    /**
     * 获取缓存中资源
     * @param bundlePath 
     * @returns 
     */
    getAssets(bundle: string, path: string, type: typeof Asset): ResCache {
        let _type = '';
        if (type) {
            _type = js.getClassName(type)
        }

        let nowTime = TimeUtils.GetTimeBySecond()
        //设一个很大的时间
        let newDateTime: number = nowTime + 60 * 60 * 24;

        let cacheKey = bundle + '/' + path


        let existIndex = this.isExistKey(cacheKey, _type, this.temporaryCache);
        let existIndex2 = this.isExistKey(cacheKey, _type, this.permanentCache);



        let res1: ResCache = null;
        let res2: ResCache = null;

        // //自动释放资源已存在
        // if (existIndex != -1) {
        //     this.temporaryCache[existIndex].releaseTime = newDateTime
        //     this.temporaryCache[existIndex].createTime = nowTime
        //     this.temporaryCache[existIndex].canRelease = true
        //     let res = this.temporaryCache[existIndex].asset
        //     if (res && res.refCount > 0) {
        //         res1 = this.temporaryCache[existIndex];
        //         LogMgr.getInstance().debug("自动释放资源已存在" + bundle + '/' + path, res.refCount)
        //     }

        // }

        // //永久资源已存在
        // if (existIndex2 != -1) {
        //     this.permanentCache[existIndex2].releaseTime = newDateTime
        //     this.permanentCache[existIndex2].createTime = nowTime
        //     this.permanentCache[existIndex2].canRelease = true
        //     let res = this.permanentCache[existIndex2].asset
        //     if (res && res.refCount > 0) {
        //         LogMgr.getInstance().debug("非自动释放资源已存在" + bundle + '/' + path, res.refCount)
        //         res2 = this.permanentCache[existIndex2];
        //     }

        // }
        return res1 ? res1 : res2 ? res2 : null;
    }




    /**
     * 是否存在引用
     * @param key 
     * @param arr 
     * @returns 
     */
    isExistKey(cachekey: string, type: string, arr: ResCache[] | ResCacheKey[]) {
        for (let i = 0; i < arr.length; i++) {
            let item = arr[i]
            if (item) {
                if (item.cachekey == cachekey && item.type == type) {
                    return i;
                }
            }

        }
        return -1;
    }
    // copied from Cocos engine souce code.
    visitAsset(asset: Asset, deps: string[]) {
        // assets generated programmatically or by user (e.g. label texture)  动态合图产生的纹理、字体纹理
        if (!asset['_uuid']) {
            if (asset instanceof SpriteFrame) {
                const SpriteFrame = asset
                if (SpriteFrame['_original']) {
                    const texture = SpriteFrame['_original']._texture;
                    texture && deps.push(texture._uuid);;
                } else {
                    const texture = SpriteFrame.texture;
                    texture && deps.push(texture['_uuid']);;
                }
            }
            return;
        }
        deps.push(asset['_uuid']);
    }

    visitComponent(comp: Component, deps: string[]) {
        const props = Object.getOwnPropertyNames(comp);
        for (let i = 0; i < props.length; i++) {
            const propName = props[i];
            if (propName === 'node' || propName === '__eventTargets') { continue; }
            const value = comp[propName];

            if (value instanceof NodePool) {
                this.visitNodePool(value, deps);
            } else
                if (typeof value === 'object' && value) {
                    if (Array.isArray(value)) {
                        for (let j = 0; j < value.length; j++) {
                            const val = value[j];
                            if (val instanceof Asset) {
                                this.visitAsset(val, deps);
                            }
                        }
                    } else if (!value.constructor || value.constructor === Object) {
                        const keys = Object.getOwnPropertyNames(value);
                        for (let j = 0; j < keys.length; j++) {
                          
                            const val = value[keys[j]];
                            if (val instanceof Asset) {
                                this.visitAsset(val, deps);
                            }
                        }
                    } else if (value instanceof Asset) {
                        this.visitAsset(value, deps);
                    }
                }
        }
    }

    visitNode(node: any, deps: string[]) {
        for (let i = 0; i < node._components.length; i++) {
            this.visitComponent(node._components[i], deps);
        }
        for (let i = 0; i < node._children.length; i++) {
            this.visitNode(node._children[i], deps);
        }
    }

    visitNodePool(nodePool: NodePool, deps: string[]) {
        const pool: Node[] = nodePool['_pool']
        for (let j = 0; j < pool.length; j++) {
            const node = pool[j];
            this.visitNode(node, deps);
        }
    }



}









